// Copyright 2009-2012 by contributors, MIT License
// vim: ts=4 sts=4 sw=4 expandtab

// Module systems magic dance
(function (definition) {
  // RequireJS
  if (typeof define == 'function') {
    define(definition);
    // YUI3
  } else if (typeof YUI == 'function') {
    YUI.add('es5', definition);
    // CommonJS and <script>
  } else {
    definition();
  }
})(function () {
  /**
   * Brings an environment as close to ECMAScript 5 compliance
   * as is possible with the facilities of erstwhile engines.
   *
   * Annotated ES5: http://es5.github.com/ (specific links below)
   * ES5 Spec: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
   * Required reading: http://javascriptweblog.wordpress.com/2011/12/05/extending-javascript-natives/
   */

  //
  // Function
  // ========
  //

  // ES-5 15.3.4.5
  // http://es5.github.com/#x15.3.4.5

  function Empty() {}

  if (!Function.prototype.bind) {
    Function.prototype.bind = function bind(that) {
      // .length is 1
      // 1. Let Target be the this value.
      var target = this;
      // 2. If IsCallable(Target) is false, throw a TypeError exception.
      if (typeof target != 'function') {
        throw new TypeError('Function.prototype.bind called on incompatible ' + target);
      }
      // 3. Let A be a new (possibly empty) internal list of all of the
      //   argument values provided after thisArg (arg1, arg2 etc), in order.
      // XXX slicedArgs will stand in for "A" if used
      var args = _Array_slice_.call(arguments, 1); // for normal call
      // 4. Let F be a new native ECMAScript object.
      // 11. Set the [[Prototype]] internal property of F to the standard
      //   built-in Function prototype object as specified in 15.3.3.1.
      // 12. Set the [[Call]] internal property of F as described in
      //   15.3.4.5.1.
      // 13. Set the [[Construct]] internal property of F as described in
      //   15.3.4.5.2.
      // 14. Set the [[HasInstance]] internal property of F as described in
      //   15.3.4.5.3.
      var bound = function () {
        if (this instanceof bound) {
          // 15.3.4.5.2 [[Construct]]
          // When the [[Construct]] internal method of a function object,
          // F that was created using the bind function is called with a
          // list of arguments ExtraArgs, the following steps are taken:
          // 1. Let target be the value of F's [[TargetFunction]]
          //   internal property.
          // 2. If target has no [[Construct]] internal method, a
          //   TypeError exception is thrown.
          // 3. Let boundArgs be the value of F's [[BoundArgs]] internal
          //   property.
          // 4. Let args be a new list containing the same values as the
          //   list boundArgs in the same order followed by the same
          //   values as the list ExtraArgs in the same order.
          // 5. Return the result of calling the [[Construct]] internal
          //   method of target providing args as the arguments.

          var result = target.apply(this, args.concat(_Array_slice_.call(arguments)));
          if (Object(result) === result) {
            return result;
          }
          return this;
        } else {
          // 15.3.4.5.1 [[Call]]
          // When the [[Call]] internal method of a function object, F,
          // which was created using the bind function is called with a
          // this value and a list of arguments ExtraArgs, the following
          // steps are taken:
          // 1. Let boundArgs be the value of F's [[BoundArgs]] internal
          //   property.
          // 2. Let boundThis be the value of F's [[BoundThis]] internal
          //   property.
          // 3. Let target be the value of F's [[TargetFunction]] internal
          //   property.
          // 4. Let args be a new list containing the same values as the
          //   list boundArgs in the same order followed by the same
          //   values as the list ExtraArgs in the same order.
          // 5. Return the result of calling the [[Call]] internal method
          //   of target providing boundThis as the this value and
          //   providing args as the arguments.

          // equiv: target.call(this, ...boundArgs, ...args)
          return target.apply(that, args.concat(_Array_slice_.call(arguments)));
        }
      };
      if (target.prototype) {
        Empty.prototype = target.prototype;
        bound.prototype = new Empty();
        // Clean up dangling references.
        Empty.prototype = null;
      }
      // XXX bound.length is never writable, so don't even try
      //
      // 15. If the [[Class]] internal property of Target is "Function", then
      //     a. Let L be the length property of Target minus the length of A.
      //     b. Set the length own property of F to either 0 or L, whichever is
      //       larger.
      // 16. Else set the length own property of F to 0.
      // 17. Set the attributes of the length own property of F to the values
      //   specified in 15.3.5.1.

      // TODO
      // 18. Set the [[Extensible]] internal property of F to true.

      // TODO
      // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
      // 20. Call the [[DefineOwnProperty]] internal method of F with
      //   arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
      //   thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
      //   false.
      // 21. Call the [[DefineOwnProperty]] internal method of F with
      //   arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
      //   [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
      //   and false.

      // TODO
      // NOTE Function objects created using Function.prototype.bind do not
      // have a prototype property or the [[Code]], [[FormalParameters]], and
      // [[Scope]] internal properties.
      // XXX can't delete prototype in pure-js.

      // 22. Return F.
      return bound;
    };
  }

  // Shortcut to an often accessed properties, in order to avoid multiple
  // dereference that costs universally.
  // _Please note: Shortcuts are defined after `Function.prototype.bind` as we
  // us it in defining shortcuts.
  var call = Function.prototype.call;
  var prototypeOfArray = Array.prototype;
  var prototypeOfObject = Object.prototype;
  var _Array_slice_ = prototypeOfArray.slice;
  // Having a toString local variable name breaks in Opera so use _toString.
  var _toString = call.bind(prototypeOfObject.toString);
  var owns = call.bind(prototypeOfObject.hasOwnProperty);

  // If JS engine supports accessors creating shortcuts.
  var defineGetter;
  var defineSetter;
  var lookupGetter;
  var lookupSetter;
  var supportsAccessors;
  if ((supportsAccessors = owns(prototypeOfObject, '__defineGetter__'))) {
    defineGetter = call.bind(prototypeOfObject.__defineGetter__);
    defineSetter = call.bind(prototypeOfObject.__defineSetter__);
    lookupGetter = call.bind(prototypeOfObject.__lookupGetter__);
    lookupSetter = call.bind(prototypeOfObject.__lookupSetter__);
  }

  //
  // Array
  // =====
  //

  // ES5 15.4.4.12
  // http://es5.github.com/#x15.4.4.12
  // Default value for second param
  // [bugfix, ielt9, old browsers]
  // IE < 9 bug: [1,2].splice(0).join("") == "" but should be "12"
  if ([1, 2].splice(0).length != 2) {
    var array_splice = Array.prototype.splice;

    if (
      (function () {
        // test IE < 9 to splice bug - see issue #138
        function makeArray(l) {
          var a = [];
          while (l--) {
            a.unshift(l);
          }
          return a;
        }

        var array = [],
          lengthBefore;

        array.splice.bind(array, 0, 0).apply(null, makeArray(20));
        array.splice.bind(array, 0, 0).apply(null, makeArray(26));

        lengthBefore = array.length; //20
        array.splice(5, 0, 'XXX'); // add one element

        if (lengthBefore + 1 == array.length) {
          return true; // has right splice implementation without bugs
        }
        // else {
        //    IE8 bug
        // }
      })()
    ) {
      //IE 6/7
      Array.prototype.splice = function (start, deleteCount) {
        if (!arguments.length) {
          return [];
        } else {
          return array_splice.apply(
            this,
            [start === void 0 ? 0 : start, deleteCount === void 0 ? this.length - start : deleteCount].concat(
              _Array_slice_.call(arguments, 2)
            )
          );
        }
      };
    } else {
      //IE8
      Array.prototype.splice = function (start, deleteCount) {
        var result,
          args = _Array_slice_.call(arguments, 2),
          addElementsCount = args.length;
        if (!arguments.length) {
          return [];
        }

        if (start === void 0) {
          // default
          start = 0;
        }
        if (deleteCount === void 0) {
          // default
          deleteCount = this.length - start;
        }

        if (addElementsCount > 0) {
          if (deleteCount <= 0) {
            if (start == this.length) {
              // tiny optimisation #1
              this.push.apply(this, args);
              return [];
            }

            if (start == 0) {
              // tiny optimisation #2
              this.unshift.apply(this, args);
              return [];
            }
          }

          // Array.prototype.splice implementation
          result = _Array_slice_.call(this, start, start + deleteCount); // delete part
          args.push.apply(args, _Array_slice_.call(this, start + deleteCount, this.length)); // right part
          args.unshift.apply(args, _Array_slice_.call(this, 0, start)); // left part

          // delete all items from this array and replace it to 'left part' + _Array_slice_.call(arguments, 2) + 'right part'
          args.unshift(0, this.length);

          array_splice.apply(this, args);

          return result;
        }

        return array_splice.call(this, start, deleteCount);
      };
    }
  }

  // ES5 15.4.4.12
  // http://es5.github.com/#x15.4.4.13
  // Return len+argCount.
  // [bugfix, ielt8]
  // IE < 8 bug: [].unshift(0) == undefined but should be "1"
  if ([].unshift(0) != 1) {
    var array_unshift = Array.prototype.unshift;
    Array.prototype.unshift = function () {
      array_unshift.apply(this, arguments);
      return this.length;
    };
  }

  // ES5 15.4.3.2
  // http://es5.github.com/#x15.4.3.2
  // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
  if (!Array.isArray) {
    Array.isArray = function isArray(obj) {
      return _toString(obj) == '[object Array]';
    };
  }

  // The IsCallable() check in the Array functions
  // has been replaced with a strict check on the
  // internal class of the object to trap cases where
  // the provided function was actually a regular
  // expression literal, which in V8 and
  // JavaScriptCore is a typeof "function".  Only in
  // V8 are regular expression literals permitted as
  // reduce parameters, so it is desirable in the
  // general case for the shim to match the more
  // strict and common behavior of rejecting regular
  // expressions.

  // ES5 15.4.4.18
  // http://es5.github.com/#x15.4.4.18
  // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/forEach

  // Check failure of by-index access of string characters (IE < 9)
  // and failure of `0 in boxedString` (Rhino)
  var boxedString = Object('a'),
    splitString = boxedString[0] != 'a' || !(0 in boxedString);

  if (!Array.prototype.forEach) {
    Array.prototype.forEach = function forEach(fun /*, thisp*/) {
      var object = toObject(this),
        self = splitString && _toString(this) == '[object String]' ? this.split('') : object,
        thisp = arguments[1],
        i = -1,
        length = self.length >>> 0;

      // If no callback function or if callback is not a callable function
      if (_toString(fun) != '[object Function]') {
        throw new TypeError(); // TODO message
      }

      while (++i < length) {
        if (i in self) {
          // Invoke the callback function with call, passing arguments:
          // context, property value, property key, thisArg object
          // context
          fun.call(thisp, self[i], i, object);
        }
      }
    };
  }

  // ES5 15.4.4.19
  // http://es5.github.com/#x15.4.4.19
  // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
  if (!Array.prototype.map) {
    Array.prototype.map = function map(fun /*, thisp*/) {
      var object = toObject(this),
        self = splitString && _toString(this) == '[object String]' ? this.split('') : object,
        length = self.length >>> 0,
        result = Array(length),
        thisp = arguments[1];

      // If no callback function or if callback is not a callable function
      if (_toString(fun) != '[object Function]') {
        throw new TypeError(fun + ' is not a function');
      }

      for (var i = 0; i < length; i++) {
        if (i in self) result[i] = fun.call(thisp, self[i], i, object);
      }
      return result;
    };
  }

  // ES5 15.4.4.20
  // http://es5.github.com/#x15.4.4.20
  // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
  if (!Array.prototype.filter) {
    Array.prototype.filter = function filter(fun /*, thisp */) {
      var object = toObject(this),
        self = splitString && _toString(this) == '[object String]' ? this.split('') : object,
        length = self.length >>> 0,
        result = [],
        value,
        thisp = arguments[1];

      // If no callback function or if callback is not a callable function
      if (_toString(fun) != '[object Function]') {
        throw new TypeError(fun + ' is not a function');
      }

      for (var i = 0; i < length; i++) {
        if (i in self) {
          value = self[i];
          if (fun.call(thisp, value, i, object)) {
            result.push(value);
          }
        }
      }
      return result;
    };
  }

  // ES5 15.4.4.16
  // http://es5.github.com/#x15.4.4.16
  // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
  if (!Array.prototype.every) {
    Array.prototype.every = function every(fun /*, thisp */) {
      var object = toObject(this),
        self = splitString && _toString(this) == '[object String]' ? this.split('') : object,
        length = self.length >>> 0,
        thisp = arguments[1];

      // If no callback function or if callback is not a callable function
      if (_toString(fun) != '[object Function]') {
        throw new TypeError(fun + ' is not a function');
      }

      for (var i = 0; i < length; i++) {
        if (i in self && !fun.call(thisp, self[i], i, object)) {
          return false;
        }
      }
      return true;
    };
  }

  // ES5 15.4.4.17
  // http://es5.github.com/#x15.4.4.17
  // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
  if (!Array.prototype.some) {
    Array.prototype.some = function some(fun /*, thisp */) {
      var object = toObject(this),
        self = splitString && _toString(this) == '[object String]' ? this.split('') : object,
        length = self.length >>> 0,
        thisp = arguments[1];

      // If no callback function or if callback is not a callable function
      if (_toString(fun) != '[object Function]') {
        throw new TypeError(fun + ' is not a function');
      }

      for (var i = 0; i < length; i++) {
        if (i in self && fun.call(thisp, self[i], i, object)) {
          return true;
        }
      }
      return false;
    };
  }

  // ES5 15.4.4.21
  // http://es5.github.com/#x15.4.4.21
  // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
  if (!Array.prototype.reduce) {
    Array.prototype.reduce = function reduce(fun /*, initial*/) {
      var object = toObject(this),
        self = splitString && _toString(this) == '[object String]' ? this.split('') : object,
        length = self.length >>> 0;

      // If no callback function or if callback is not a callable function
      if (_toString(fun) != '[object Function]') {
        throw new TypeError(fun + ' is not a function');
      }

      // no value to return if no initial value and an empty array
      if (!length && arguments.length == 1) {
        throw new TypeError('reduce of empty array with no initial value');
      }

      var i = 0;
      var result;
      if (arguments.length >= 2) {
        result = arguments[1];
      } else {
        do {
          if (i in self) {
            result = self[i++];
            break;
          }

          // if array contains no values, no initial value to return
          if (++i >= length) {
            throw new TypeError('reduce of empty array with no initial value');
          }
        } while (true);
      }

      for (; i < length; i++) {
        if (i in self) {
          result = fun.call(void 0, result, self[i], i, object);
        }
      }

      return result;
    };
  }

  // ES5 15.4.4.22
  // http://es5.github.com/#x15.4.4.22
  // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
  if (!Array.prototype.reduceRight) {
    Array.prototype.reduceRight = function reduceRight(fun /*, initial*/) {
      var object = toObject(this),
        self = splitString && _toString(this) == '[object String]' ? this.split('') : object,
        length = self.length >>> 0;

      // If no callback function or if callback is not a callable function
      if (_toString(fun) != '[object Function]') {
        throw new TypeError(fun + ' is not a function');
      }

      // no value to return if no initial value, empty array
      if (!length && arguments.length == 1) {
        throw new TypeError('reduceRight of empty array with no initial value');
      }

      var result,
        i = length - 1;
      if (arguments.length >= 2) {
        result = arguments[1];
      } else {
        do {
          if (i in self) {
            result = self[i--];
            break;
          }

          // if array contains no values, no initial value to return
          if (--i < 0) {
            throw new TypeError('reduceRight of empty array with no initial value');
          }
        } while (true);
      }

      if (i < 0) {
        return result;
      }

      do {
        if (i in this) {
          result = fun.call(void 0, result, self[i], i, object);
        }
      } while (i--);

      return result;
    };
  }

  // ES5 15.4.4.14
  // http://es5.github.com/#x15.4.4.14
  // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
  if (!Array.prototype.indexOf || [0, 1].indexOf(1, 2) != -1) {
    Array.prototype.indexOf = function indexOf(sought /*, fromIndex */) {
      var self = splitString && _toString(this) == '[object String]' ? this.split('') : toObject(this),
        length = self.length >>> 0;

      if (!length) {
        return -1;
      }

      var i = 0;
      if (arguments.length > 1) {
        i = toInteger(arguments[1]);
      }

      // handle negative indices
      i = i >= 0 ? i : Math.max(0, length + i);
      for (; i < length; i++) {
        if (i in self && self[i] === sought) {
          return i;
        }
      }
      return -1;
    };
  }

  // ES5 15.4.4.15
  // http://es5.github.com/#x15.4.4.15
  // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf
  if (!Array.prototype.lastIndexOf || [0, 1].lastIndexOf(0, -3) != -1) {
    Array.prototype.lastIndexOf = function lastIndexOf(sought /*, fromIndex */) {
      var self = splitString && _toString(this) == '[object String]' ? this.split('') : toObject(this),
        length = self.length >>> 0;

      if (!length) {
        return -1;
      }
      var i = length - 1;
      if (arguments.length > 1) {
        i = Math.min(i, toInteger(arguments[1]));
      }
      // handle negative indices
      i = i >= 0 ? i : length - Math.abs(i);
      for (; i >= 0; i--) {
        if (i in self && sought === self[i]) {
          return i;
        }
      }
      return -1;
    };
  }

  //
  // Object
  // ======
  //

  // ES5 15.2.3.14
  // http://es5.github.com/#x15.2.3.14
  if (!Object.keys) {
    // http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
    var hasDontEnumBug = true,
      dontEnums = ['toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor'],
      dontEnumsLength = dontEnums.length;

    for (var key in { toString: null }) {
      hasDontEnumBug = false;
    }

    Object.keys = function keys(object) {
      if ((typeof object != 'object' && typeof object != 'function') || object === null) {
        throw new TypeError('Object.keys called on a non-object');
      }

      var keys = [];
      for (var name in object) {
        if (owns(object, name)) {
          keys.push(name);
        }
      }

      if (hasDontEnumBug) {
        for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
          var dontEnum = dontEnums[i];
          if (owns(object, dontEnum)) {
            keys.push(dontEnum);
          }
        }
      }
      return keys;
    };
  }

  //
  // Date
  // ====
  //

  // ES5 15.9.5.43
  // http://es5.github.com/#x15.9.5.43
  // This function returns a String value represent the instance in time
  // represented by this Date object. The format of the String is the Date Time
  // string format defined in 15.9.1.15. All fields are present in the String.
  // The time zone is always UTC, denoted by the suffix Z. If the time value of
  // this object is not a finite Number a RangeError exception is thrown.
  var negativeDate = -62198755200000,
    negativeYearString = '-000001';
  if (!Date.prototype.toISOString || new Date(negativeDate).toISOString().indexOf(negativeYearString) === -1) {
    Date.prototype.toISOString = function toISOString() {
      var result, length, value, year, month;
      if (!isFinite(this)) {
        throw new RangeError('Date.prototype.toISOString called on non-finite value.');
      }

      year = this.getUTCFullYear();

      month = this.getUTCMonth();
      // see https://github.com/kriskowal/es5-shim/issues/111
      year += Math.floor(month / 12);
      month = ((month % 12) + 12) % 12;

      // the date time string format is specified in 15.9.1.15.
      result = [month + 1, this.getUTCDate(), this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds()];
      year = (year < 0 ? '-' : year > 9999 ? '+' : '') + ('00000' + Math.abs(year)).slice(0 <= year && year <= 9999 ? -4 : -6);

      length = result.length;
      while (length--) {
        value = result[length];
        // pad months, days, hours, minutes, and seconds to have two
        // digits.
        if (value < 10) {
          result[length] = '0' + value;
        }
      }
      // pad milliseconds to have three digits.
      return (
        year +
        '-' +
        result.slice(0, 2).join('-') +
        'T' +
        result.slice(2).join(':') +
        '.' +
        ('000' + this.getUTCMilliseconds()).slice(-3) +
        'Z'
      );
    };
  }

  // ES5 15.9.5.44
  // http://es5.github.com/#x15.9.5.44
  // This function provides a String representation of a Date object for use by
  // JSON.stringify (15.12.3).
  var dateToJSONIsSupported = false;
  try {
    dateToJSONIsSupported =
      Date.prototype.toJSON &&
      new Date(NaN).toJSON() === null &&
      new Date(negativeDate).toJSON().indexOf(negativeYearString) !== -1 &&
      Date.prototype.toJSON.call({
        // generic
        toISOString: function () {
          return true;
        },
      });
  } catch (e) {}
  if (!dateToJSONIsSupported) {
    Date.prototype.toJSON = function toJSON(key) {
      // When the toJSON method is called with argument key, the following
      // steps are taken:

      // 1.  Let O be the result of calling ToObject, giving it the this
      // value as its argument.
      // 2. Let tv be toPrimitive(O, hint Number).
      var o = Object(this),
        tv = toPrimitive(o),
        toISO;
      // 3. If tv is a Number and is not finite, return null.
      if (typeof tv === 'number' && !isFinite(tv)) {
        return null;
      }
      // 4. Let toISO be the result of calling the [[Get]] internal method of
      // O with argument "toISOString".
      toISO = o.toISOString;
      // 5. If IsCallable(toISO) is false, throw a TypeError exception.
      if (typeof toISO != 'function') {
        throw new TypeError('toISOString property is not callable');
      }
      // 6. Return the result of calling the [[Call]] internal method of
      //  toISO with O as the this value and an empty argument list.
      return toISO.call(o);

      // NOTE 1 The argument is ignored.

      // NOTE 2 The toJSON function is intentionally generic; it does not
      // require that its this value be a Date object. Therefore, it can be
      // transferred to other kinds of objects for use as a method. However,
      // it does require that any such object have a toISOString method. An
      // object is free to use the argument key to filter its
      // stringification.
    };
  }

  // ES5 15.9.4.2
  // http://es5.github.com/#x15.9.4.2
  // based on work shared by Daniel Friesen (dantman)
  // http://gist.github.com/303249
  if (!Date.parse || 'Date.parse is buggy') {
    // XXX global assignment won't work in embeddings that use
    // an alternate object for the context.
    Date = (function (NativeDate) {
      // Date.length === 7
      function Date(Y, M, D, h, m, s, ms) {
        var length = arguments.length;
        if (this instanceof NativeDate) {
          var date =
            length == 1 && String(Y) === Y // isString(Y)
              ? // We explicitly pass it through parse:
                new NativeDate(Date.parse(Y))
              : // We have to manually make calls depending on argument
              // length here
              length >= 7
              ? new NativeDate(Y, M, D, h, m, s, ms)
              : length >= 6
              ? new NativeDate(Y, M, D, h, m, s)
              : length >= 5
              ? new NativeDate(Y, M, D, h, m)
              : length >= 4
              ? new NativeDate(Y, M, D, h)
              : length >= 3
              ? new NativeDate(Y, M, D)
              : length >= 2
              ? new NativeDate(Y, M)
              : length >= 1
              ? new NativeDate(Y)
              : new NativeDate();
          // Prevent mixups with unfixed Date object
          date.constructor = Date;
          return date;
        }
        return NativeDate.apply(this, arguments);
      }

      // 15.9.1.15 Date Time String Format.
      var isoDateExpression = new RegExp(
        '^' +
          '(\\d{4}|[+-]\\d{6})' + // four-digit year capture or sign +
          // 6-digit extended year
          '(?:-(\\d{2})' + // optional month capture
          '(?:-(\\d{2})' + // optional day capture
          '(?:' + // capture hours:minutes:seconds.milliseconds
          'T(\\d{2})' + // hours capture
          ':(\\d{2})' + // minutes capture
          '(?:' + // optional :seconds.milliseconds
          ':(\\d{2})' + // seconds capture
          '(?:(\\.\\d{1,}))?' + // milliseconds capture
          ')?' +
          '(' + // capture UTC offset component
          'Z|' + // UTC capture
          '(?:' + // offset specifier +/-hours:minutes
          '([-+])' + // sign capture
          '(\\d{2})' + // hours offset capture
          ':(\\d{2})' + // minutes offset capture
          ')' +
          ')?)?)?)?' +
          '$'
      );

      var months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];

      function dayFromMonth(year, month) {
        var t = month > 1 ? 1 : 0;
        return (
          months[month] +
          Math.floor((year - 1969 + t) / 4) -
          Math.floor((year - 1901 + t) / 100) +
          Math.floor((year - 1601 + t) / 400) +
          365 * (year - 1970)
        );
      }

      // Copy any custom methods a 3rd party library may have added
      for (var key in NativeDate) {
        Date[key] = NativeDate[key];
      }

      // Copy "native" methods explicitly; they may be non-enumerable
      Date.now = NativeDate.now;
      Date.UTC = NativeDate.UTC;
      Date.prototype = NativeDate.prototype;
      Date.prototype.constructor = Date;

      // Upgrade Date.parse to handle simplified ISO 8601 strings
      Date.parse = function parse(string) {
        var match = isoDateExpression.exec(string);
        if (match) {
          // parse months, days, hours, minutes, seconds, and milliseconds
          // provide default values if necessary
          // parse the UTC offset component
          var year = Number(match[1]),
            month = Number(match[2] || 1) - 1,
            day = Number(match[3] || 1) - 1,
            hour = Number(match[4] || 0),
            minute = Number(match[5] || 0),
            second = Number(match[6] || 0),
            millisecond = Math.floor(Number(match[7] || 0) * 1000),
            // When time zone is missed, local offset should be used
            // (ES 5.1 bug)
            // see https://bugs.ecmascript.org/show_bug.cgi?id=112
            offset = !match[4] || match[8] ? 0 : Number(new NativeDate(1970, 0)),
            signOffset = match[9] === '-' ? 1 : -1,
            hourOffset = Number(match[10] || 0),
            minuteOffset = Number(match[11] || 0),
            result;
          if (
            hour < (minute > 0 || second > 0 || millisecond > 0 ? 24 : 25) &&
            minute < 60 &&
            second < 60 &&
            millisecond < 1000 &&
            month > -1 &&
            month < 12 &&
            hourOffset < 24 &&
            minuteOffset < 60 && // detect invalid offsets
            day > -1 &&
            day < dayFromMonth(year, month + 1) - dayFromMonth(year, month)
          ) {
            result = ((dayFromMonth(year, month) + day) * 24 + hour + hourOffset * signOffset) * 60;
            result = ((result + minute + minuteOffset * signOffset) * 60 + second) * 1000 + millisecond + offset;
            if (-8.64e15 <= result && result <= 8.64e15) {
              return result;
            }
          }
          return NaN;
        }
        return NativeDate.parse.apply(this, arguments);
      };

      return Date;
    })(Date);
  }

  // ES5 15.9.4.4
  // http://es5.github.com/#x15.9.4.4
  if (!Date.now) {
    Date.now = function now() {
      return new Date().getTime();
    };
  }

  //
  // Number
  // ======
  //

  // ES5.1 15.7.4.5
  // http://es5.github.com/#x15.7.4.5
  if (
    !Number.prototype.toFixed ||
    (0.00008).toFixed(3) !== '0.000' ||
    (0.9).toFixed(0) === '0' ||
    (1.255).toFixed(2) !== '1.25' ||
    (1000000000000000128).toFixed(0) !== '1000000000000000128'
  ) {
    // Hide these variables and functions
    (function () {
      var base, size, data, i;

      base = 1e7;
      size = 6;
      data = [0, 0, 0, 0, 0, 0];

      function multiply(n, c) {
        var i = -1;
        while (++i < size) {
          c += n * data[i];
          data[i] = c % base;
          c = Math.floor(c / base);
        }
      }

      function divide(n) {
        var i = size,
          c = 0;
        while (--i >= 0) {
          c += data[i];
          data[i] = Math.floor(c / n);
          c = (c % n) * base;
        }
      }

      function toString() {
        var i = size;
        var s = '';
        while (--i >= 0) {
          if (s !== '' || i === 0 || data[i] !== 0) {
            var t = String(data[i]);
            if (s === '') {
              s = t;
            } else {
              s += '0000000'.slice(0, 7 - t.length) + t;
            }
          }
        }
        return s;
      }

      function pow(x, n, acc) {
        return n === 0 ? acc : n % 2 === 1 ? pow(x, n - 1, acc * x) : pow(x * x, n / 2, acc);
      }

      function log(x) {
        var n = 0;
        while (x >= 4096) {
          n += 12;
          x /= 4096;
        }
        while (x >= 2) {
          n += 1;
          x /= 2;
        }
        return n;
      }

      Number.prototype.toFixed = function (fractionDigits) {
        var f, x, s, m, e, z, j, k;

        // Test for NaN and round fractionDigits down
        f = Number(fractionDigits);
        f = f !== f ? 0 : Math.floor(f);

        if (f < 0 || f > 20) {
          throw new RangeError('Number.toFixed called with invalid number of decimals');
        }

        x = Number(this);

        // Test for NaN
        if (x !== x) {
          return 'NaN';
        }

        // If it is too big or small, return the string value of the number
        if (x <= -1e21 || x >= 1e21) {
          return String(x);
        }

        s = '';

        if (x < 0) {
          s = '-';
          x = -x;
        }

        m = '0';

        if (x > 1e-21) {
          // 1e-21 < x < 1e21
          // -70 < log2(x) < 70
          e = log(x * pow(2, 69, 1)) - 69;
          z = e < 0 ? x * pow(2, -e, 1) : x / pow(2, e, 1);
          z *= 0x10000000000000; // Math.pow(2, 52);
          e = 52 - e;

          // -18 < e < 122
          // x = z / 2 ^ e
          if (e > 0) {
            multiply(0, z);
            j = f;

            while (j >= 7) {
              multiply(1e7, 0);
              j -= 7;
            }

            multiply(pow(10, j, 1), 0);
            j = e - 1;

            while (j >= 23) {
              divide(1 << 23);
              j -= 23;
            }

            divide(1 << j);
            multiply(1, 1);
            divide(2);
            m = toString();
          } else {
            multiply(0, z);
            multiply(1 << -e, 0);
            m = toString() + '0.00000000000000000000'.slice(2, 2 + f);
          }
        }

        if (f > 0) {
          k = m.length;

          if (k <= f) {
            m = s + '0.0000000000000000000'.slice(0, f - k + 2) + m;
          } else {
            m = s + m.slice(0, k - f) + '.' + m.slice(k - f);
          }
        } else {
          m = s + m;
        }

        return m;
      };
    })();
  }

  //
  // String
  // ======
  //

  // ES5 15.5.4.14
  // http://es5.github.com/#x15.5.4.14

  // [bugfix, IE lt 9, firefox 4, Konqueror, Opera, obscure browsers]
  // Many browsers do not split properly with regular expressions or they
  // do not perform the split correctly under obscure conditions.
  // See http://blog.stevenlevithan.com/archives/cross-browser-split
  // I've tested in many browsers and this seems to cover the deviant ones:
  //    'ab'.split(/(?:ab)*/) should be ["", ""], not [""]
  //    '.'.split(/(.?)(.?)/) should be ["", ".", "", ""], not ["", ""]
  //    'tesst'.split(/(s)*/) should be ["t", undefined, "e", "s", "t"], not
  //       [undefined, "t", undefined, "e", ...]
  //    ''.split(/.?/) should be [], not [""]
  //    '.'.split(/()()/) should be ["."], not ["", "", "."]

  var string_split = String.prototype.split;
  if (
    'ab'.split(/(?:ab)*/).length !== 2 ||
    '.'.split(/(.?)(.?)/).length !== 4 ||
    'tesst'.split(/(s)*/)[1] === 't' ||
    ''.split(/.?/).length === 0 ||
    '.'.split(/()()/).length > 1
  ) {
    (function () {
      var compliantExecNpcg = /()??/.exec('')[1] === void 0; // NPCG: nonparticipating capturing group

      String.prototype.split = function (separator, limit) {
        var string = this;
        if (separator === void 0 && limit === 0) return [];

        // If `separator` is not a regex, use native split
        if (Object.prototype.toString.call(separator) !== '[object RegExp]') {
          return string_split.apply(this, arguments);
        }

        var output = [],
          flags =
            (separator.ignoreCase ? 'i' : '') +
            (separator.multiline ? 'm' : '') +
            (separator.extended ? 'x' : '') + // Proposed for ES6
            (separator.sticky ? 'y' : ''), // Firefox 3+
          lastLastIndex = 0,
          // Make `global` and avoid `lastIndex` issues by working with a copy
          separator = new RegExp(separator.source, flags + 'g'),
          separator2,
          match,
          lastIndex,
          lastLength;
        string += ''; // Type-convert
        if (!compliantExecNpcg) {
          // Doesn't need flags gy, but they don't hurt
          separator2 = new RegExp('^' + separator.source + '$(?!\\s)', flags);
        }
        /* Values for `limit`, per the spec:
         * If undefined: 4294967295 // Math.pow(2, 32) - 1
         * If 0, Infinity, or NaN: 0
         * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
         * If negative number: 4294967296 - Math.floor(Math.abs(limit))
         * If other: Type-convert, then use the above rules
         */
        limit =
          limit === void 0
            ? -1 >>> 0 // Math.pow(2, 32) - 1
            : limit >>> 0; // ToUint32(limit)
        while ((match = separator.exec(string))) {
          // `separator.lastIndex` is not reliable cross-browser
          lastIndex = match.index + match[0].length;
          if (lastIndex > lastLastIndex) {
            output.push(string.slice(lastLastIndex, match.index));
            // Fix browsers whose `exec` methods don't consistently return `undefined` for
            // nonparticipating capturing groups
            if (!compliantExecNpcg && match.length > 1) {
              match[0].replace(separator2, function () {
                for (var i = 1; i < arguments.length - 2; i++) {
                  if (arguments[i] === void 0) {
                    match[i] = void 0;
                  }
                }
              });
            }
            if (match.length > 1 && match.index < string.length) {
              Array.prototype.push.apply(output, match.slice(1));
            }
            lastLength = match[0].length;
            lastLastIndex = lastIndex;
            if (output.length >= limit) {
              break;
            }
          }
          if (separator.lastIndex === match.index) {
            separator.lastIndex++; // Avoid an infinite loop
          }
        }
        if (lastLastIndex === string.length) {
          if (lastLength || !separator.test('')) {
            output.push('');
          }
        } else {
          output.push(string.slice(lastLastIndex));
        }
        return output.length > limit ? output.slice(0, limit) : output;
      };
    })();

    // [bugfix, chrome]
    // If separator is undefined, then the result array contains just one String,
    // which is the this value (converted to a String). If limit is not undefined,
    // then the output array is truncated so that it contains no more than limit
    // elements.
    // "0".split(undefined, 0) -> []
  } else if ('0'.split(void 0, 0).length) {
    String.prototype.split = function (separator, limit) {
      if (separator === void 0 && limit === 0) return [];
      return string_split.apply(this, arguments);
    };
  }

  // ECMA-262, 3rd B.2.3
  // Note an ECMAScript standart, although ECMAScript 3rd Edition has a
  // non-normative section suggesting uniform semantics and it should be
  // normalized across all browsers
  // [bugfix, IE lt 9] IE < 9 substr() with negative value not working in IE
  if (''.substr && '0b'.substr(-1) !== 'b') {
    var string_substr = String.prototype.substr;
    /**
     *  Get the substring of a string
     *  @param  {integer}  start   where to start the substring
     *  @param  {integer}  length  how many characters to return
     *  @return {string}
     */
    String.prototype.substr = function (start, length) {
      return string_substr.call(this, start < 0 ? ((start = this.length + start) < 0 ? 0 : start) : start, length);
    };
  }

  // ES5 15.5.4.20
  // http://es5.github.com/#x15.5.4.20
  var ws =
    '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003' +
    '\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028' +
    '\u2029\uFEFF';
  if (!String.prototype.trim || ws.trim()) {
    // http://blog.stevenlevithan.com/archives/faster-trim-javascript
    // http://perfectionkills.com/whitespace-deviations/
    ws = '[' + ws + ']';
    var trimBeginRegexp = new RegExp('^' + ws + ws + '*'),
      trimEndRegexp = new RegExp(ws + ws + '*$');
    String.prototype.trim = function trim() {
      if (this === void 0 || this === null) {
        throw new TypeError("can't convert " + this + ' to object');
      }
      return String(this).replace(trimBeginRegexp, '').replace(trimEndRegexp, '');
    };
  }

  //
  // Util
  // ======
  //

  // ES5 9.4
  // http://es5.github.com/#x9.4
  // http://jsperf.com/to-integer

  function toInteger(n) {
    n = +n;
    if (n !== n) {
      // isNaN
      n = 0;
    } else if (n !== 0 && n !== 1 / 0 && n !== -(1 / 0)) {
      n = (n > 0 || -1) * Math.floor(Math.abs(n));
    }
    return n;
  }

  function isPrimitive(input) {
    var type = typeof input;
    return input === null || type === 'undefined' || type === 'boolean' || type === 'number' || type === 'string';
  }

  function toPrimitive(input) {
    var val, valueOf, toString;
    if (isPrimitive(input)) {
      return input;
    }
    valueOf = input.valueOf;
    if (typeof valueOf === 'function') {
      val = valueOf.call(input);
      if (isPrimitive(val)) {
        return val;
      }
    }
    toString = input.toString;
    if (typeof toString === 'function') {
      val = toString.call(input);
      if (isPrimitive(val)) {
        return val;
      }
    }
    throw new TypeError();
  }

  // ES5 9.9
  // http://es5.github.com/#x9.9
  var toObject = function (o) {
    if (o == null) {
      // this matches both null and undefined
      throw new TypeError("can't convert " + o + ' to object');
    }
    return Object(o);
  };
});
